package com.shanzmoo.core.manager;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.shanzmoo.base.constant.e.SysBillTypeEnum;
import com.shanzmoo.core.util.RedisCoreKeyUtils;
import com.shanzmoo.db.product.service.IProductSkuService;
import com.shanzmoo.db.product.service.IProductSpuService;
import com.shanzmoo.db.sys.entity.SysBillCountEntity;
import com.shanzmoo.db.sys.service.ISysBillCountService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;

/**
 * 编码管理器
 *
 * @author: Gzhao 2020/12/19
 */
@Slf4j
@Component
public class CodeManager {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private Redisson redisson;
    @Autowired
    private IProductSpuService productSpuService;
    @Autowired
    private IProductSkuService productSkuService;
    @Autowired
    private ISysBillCountService sysBillCountService;

    /**
     * 生成商品spu
     * (提醒：使用时需校验重复项)
     *
     * @return
     */
    public String generateSpu(Integer categoryId, Integer brandId) {
        String lockKey = RedisCoreKeyUtils.installLockKey("SPU_" + categoryId + "_" + brandId);

        RLock redissonLock = redisson.getLock(lockKey);
        try {
            redissonLock.lock();

            StringBuilder spu = new StringBuilder("P");
            spu.append(categoryId);
            spu.append('B');
            spu.append(brandId);
            spu.append('N');

            String spuIndexKey = RedisCoreKeyUtils.installSpuIndexKey(spu.toString());
            String value = stringRedisTemplate.opsForValue().get(spuIndexKey);

            Integer index;
            if (value == null){
                index = productSpuService.getSpuIndex(categoryId, brandId);
            } else {
                index = Integer.parseInt(value);
            }
            spu.append(++index);

            stringRedisTemplate.opsForValue().set(spuIndexKey, "" + index, 2, TimeUnit.HOURS);
            return spu.toString();
        } finally {
            redissonLock.unlock();
        }
    }

    /**
     * 生成商品sku
     * (提醒：使用时需校验重复项)
     *
     * @return
     */
    public String generateSku(String spu) {
        String lockKey = RedisCoreKeyUtils.installLockKey("SKU_" + spu);

        RLock redissonLock = redisson.getLock(lockKey);
        try {
            redissonLock.lock();
            StringBuilder sku = new StringBuilder("K");
            if (spu.contains("P")) {
                spu = spu.replace("P", "");
            }
            sku.append(spu);
            sku.append('S');

            String skuIndexKey = RedisCoreKeyUtils.installSkuIndexKey(sku.toString());
            String value = stringRedisTemplate.opsForValue().get(skuIndexKey);

            Integer index;
            if (value == null){
                index = productSkuService.getSkuIndex(spu);
            } else {
                index = Integer.parseInt(value);
            }
            sku.append(++index);

            stringRedisTemplate.opsForValue().set(skuIndexKey, "" + index, 2, TimeUnit.HOURS);
            return sku.toString();
        } finally {
            redissonLock.unlock();
        }
    }

    /**
     * 生成 单据号 ： 前缀  + 年月日（210304） + 4位顺序号
     *
     * @author Gzhao 2021/3/4
     * @param
     * @return
     */
    public String generateBillNo(SysBillTypeEnum typeEnum){
        Assert.isTrue(null != typeEnum, "单据类型不允许为空");
        String lockKey = RedisCoreKeyUtils.installLockKey("bill_no_" + typeEnum.getCode());

        RLock redissonLock = redisson.getLock(lockKey);
        try {
            redissonLock.lock();

            SysBillCountEntity billCountEntity = sysBillCountService.getByBillType(typeEnum.getCode());
            Assert.isTrue(null != billCountEntity, "数据库未查找到单据类型");
            Assert.isTrue(billCountEntity.getCount() < 9999, "单据流水号过大");

            StringBuilder stb = new StringBuilder(billCountEntity.getPrefixion());
            stb.append(DateUtil.format(LocalDateTime.now(), "yyMMdd"));
            stb.append(StrUtil.fillBefore(String.valueOf(billCountEntity.getCount()), '0', 4));


            sysBillCountService.updateBillCount(typeEnum.getCode());

            return stb.toString();
        } finally {
            redissonLock.unlock();
        }
    }

}
